package com.papers.juan.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.amazonaws.util.IOUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.papers.common.constant.DictTypeConstants;
import com.papers.common.constant.PaperConstants;
import com.papers.common.constant.QuestionConstants;
import com.papers.common.core.domain.PageQuery;
import com.papers.common.core.domain.entity.SysDictData;
import com.papers.common.core.page.TableDataInfo;
import com.papers.common.enums.GeneratePaperType;
import com.papers.common.enums.PaperRecordType;
import com.papers.common.enums.YesOrNo;
import com.papers.common.exception.ServiceException;
import com.papers.common.helper.LoginHelper;
import com.papers.common.utils.AesUtil;
import com.papers.common.utils.DateUtils;
import com.papers.common.utils.JsonUtils;
import com.papers.common.utils.StringUtils;
import com.papers.juan.domain.TJuanPaper;
import com.papers.juan.domain.TJuanPaperRecord;
import com.papers.juan.domain.TJuanQuestion;
import com.papers.juan.domain.TJuanQuestionRepos;
import com.papers.juan.domain.TJuanReposRelated;
import com.papers.juan.domain.bean.CourseKnowledgeCodeBean;
import com.papers.juan.domain.bean.PaperDetailBean;
import com.papers.juan.domain.bean.PaperImportBean;
import com.papers.juan.domain.bean.PaperJsonBean;
import com.papers.juan.domain.bean.PaperQuestionInfoBean;
import com.papers.juan.domain.bean.PaperStatisticsBean;
import com.papers.juan.domain.bean.PaperStatisticsFieldBean;
import com.papers.juan.domain.bean.QuestionInfoBean;
import com.papers.juan.domain.bean.QuestionTypeJsonBean;
import com.papers.juan.domain.bean.RandomTakeOutPaperBean;
import com.papers.juan.domain.bo.TJuanPaperBo;
import com.papers.juan.domain.vo.QuestionVO;
import com.papers.juan.domain.vo.TJuanPaperVo;
import com.papers.juan.mapper.TJuanPaperMapper;
import com.papers.juan.mapper.TJuanPaperRecordMapper;
import com.papers.juan.mapper.TJuanQuestionMapper;
import com.papers.juan.mapper.TJuanQuestionReposMapper;
import com.papers.juan.mapper.TJuanReposRelatedMapper;
import com.papers.juan.service.ITJuanPaperService;
import com.papers.system.domain.TJuanChapter;
import com.papers.system.domain.TJuanCourse;
import com.papers.system.domain.TJuanKnowledge;
import com.papers.system.domain.TJuanMajor;
import com.papers.system.mapper.SysDictDataMapper;
import com.papers.system.mapper.TJuanChapterMapper;
import com.papers.system.mapper.TJuanCourseMapper;
import com.papers.system.mapper.TJuanKnowledgeMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 试卷Service业务层处理
 *
 * @author papers
 * @date 2022-10-08
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class TJuanPaperServiceImpl implements ITJuanPaperService {

    private final TJuanPaperMapper tJuanPaperMapper;

    private final TJuanPaperRecordMapper tJuanPaperRecordMapper;

    private final TJuanQuestionReposMapper tJuanQuestionReposMapper;

    private final TJuanCourseMapper tJuanCourseMapper;

    private final TJuanChapterMapper tJuanChapterMapper;

    private final TJuanKnowledgeMapper tJuanKnowledgeMapper;

    private final SysDictDataMapper sysDictDataMapper;

    private final TJuanQuestionMapper tJuanQuestionMapper;

    private final TJuanReposRelatedMapper tJuanReposRelatedMapper;

    /**
     * 查询试卷
     */
    @Override
    public TJuanPaperVo queryById(String paperId) {
        return tJuanPaperMapper.selectVoById(paperId);
    }

    /**
     * 查询试卷列表
     */
    @Override
    public TableDataInfo<TJuanPaperVo> queryPageList(TJuanPaperBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<TJuanPaper> lqw = buildQueryWrapper(bo);
//        Page<TJuanPaperVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        Page<TJuanPaperVo> result = tJuanPaperMapper.queryPageList(bo, pageQuery.build());
        return TableDataInfo.build(result);
    }

    /**
     * 查询试卷列表
     */
    @Override
    public List<TJuanPaperVo> queryList(TJuanPaperBo bo) {
        return tJuanPaperMapper.queryPageList(bo, new PageQuery().build()).getRecords();
    }

    private LambdaQueryWrapper<TJuanPaper> buildQueryWrapper(TJuanPaperBo bo) {
//        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<TJuanPaper> lqw = Wrappers.lambdaQuery();
        lqw.eq(StringUtils.isNotBlank(bo.getCourseId()), TJuanPaper::getCourseId, bo.getCourseId());
        lqw.eq(StringUtils.isNotBlank(bo.getPaperCode()), TJuanPaper::getPaperCode, bo.getPaperCode());
        lqw.like(StringUtils.isNotBlank(bo.getPaperName()), TJuanPaper::getPaperName, bo.getPaperName());
        lqw.eq(StringUtils.isNotBlank(bo.getPaperType()), TJuanPaper::getPaperType, bo.getPaperType());
        lqw.eq(StringUtils.isNotBlank(bo.getPaperSubject()), TJuanPaper::getPaperSubject, bo.getPaperSubject());
        lqw.eq(StringUtils.isNotBlank(bo.getPaperDes()), TJuanPaper::getPaperDes, bo.getPaperDes());
        lqw.eq(StringUtils.isNotBlank(bo.getPaperStatus()), TJuanPaper::getPaperStatus, bo.getPaperStatus());
        lqw.eq(StringUtils.isNotBlank(bo.getPaperEndTime()), TJuanPaper::getPaperEndTime, bo.getPaperEndTime());
        lqw.eq(StringUtils.isNotBlank(bo.getCreatedBy()), TJuanPaper::getCreatedBy, bo.getCreatedBy());
        lqw.eq(bo.getCreatedTime() != null, TJuanPaper::getCreatedTime, bo.getCreatedTime());
        lqw.eq(StringUtils.isNotBlank(bo.getUpdatedBy()), TJuanPaper::getUpdatedBy, bo.getUpdatedBy());
        lqw.eq(bo.getUpdatedTime() != null, TJuanPaper::getUpdatedTime, bo.getUpdatedTime());
        return lqw;
    }

    /**
     * 新增试卷
     *
     * @param juanPaperBo 试卷信息
     * @return 是否新增成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean insertByBo(TJuanPaperBo juanPaperBo) {
        // 试卷信息
        TJuanPaper juanPaper = BeanUtil.toBean(juanPaperBo, TJuanPaper.class);

//        List<PaperQuestionInfoBean> questionInfoBeans = getQuestions(juanPaper.getPaperQuestionInfo());
//        List<String> questionIds = questionInfoBeans.stream().map(PaperQuestionInfoBean::getQuestionId).collect(Collectors.toList());
//        List<TJuanQuestion> questions = tJuanQuestionMapper.selectBatchIds(questionIds);
//        BigDecimal totalDifficult = questions.stream().map(TJuanQuestion::getQuestionDifficult).reduce(BigDecimal.ZERO, BigDecimal::add);
        // 各题目难度系数平均值得出
//        BigDecimal paperEasy = totalDifficult.divide(BigDecimal.valueOf(questions.size()), 2, BigDecimal.ROUND_HALF_UP);
//        juanPaper.setPaperEasy(paperEasy);

        juanPaper.setPaperType(GeneratePaperType.MANUAL.getKey());
        juanPaper.setPaperCode(getPaperCode(juanPaper));
        juanPaper.setIsTakenOut(YesOrNo.NO.getKey());
        juanPaper.setCreatedBy(String.valueOf(LoginHelper.getUserId()));
        juanPaper.setCreatedTime(DateUtils.getNowDate());
//        validEntityBeforeSave(juanPaper);
        boolean flag = tJuanPaperMapper.insert(juanPaper) > 0;
        if (flag) {
            juanPaper.setPaperId(juanPaper.getPaperId());
            // 新增试卷维护记录
//            insertPaperRecordBatch(PaperRecordType.MANUAL_INSERT, Collections.singletonList(juanPaper));
        }
        return flag;
    }

    /**
     * 修改试卷
     */
    @Override
    public Boolean updateByBo(TJuanPaperBo bo) {
        TJuanPaper update = BeanUtil.toBean(bo, TJuanPaper.class);
        update.setUpdatedBy(String.valueOf(LoginHelper.getUserId()));
        update.setUpdatedTime(DateUtils.getNowDate());
        // 保存前的数据校验
//        validEntityBeforeSave(update);
        Boolean flag = tJuanPaperMapper.updateById(update) > 0;
        if (flag) {
            // 新增试卷维护记录
//            insertPaperRecordBatch(PaperRecordType.UPDATE, Collections.singletonList(update));
        }
        return flag;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(TJuanPaper entity) {
        // 校验试卷编码是否存在
//        boolean existPaperCode = tJuanPaperMapper.exists(new LambdaQueryWrapper<TJuanPaper>()
//            .eq(TJuanPaper::getPaperCode, entity.getPaperCode())
//            .ne(ObjectUtil.isNotNull(entity.getPaperId()), TJuanPaper::getPaperId, entity.getPaperId()));
//        if (existPaperCode) {
//            throw new ServiceException("操作失败，试卷编码'" + entity.getPaperCode() + "'已存在！");
//        }

        // 校验试卷名称是否存在
        boolean existPaperName = tJuanPaperMapper.exists(new LambdaQueryWrapper<TJuanPaper>()
            .eq(TJuanPaper::getPaperName, entity.getPaperName())
            .ne(ObjectUtil.isNotNull(entity.getPaperId()), TJuanPaper::getPaperId, entity.getPaperId()));
        if (existPaperName) {
            throw new ServiceException("操作失败，试卷名称'" + entity.getPaperName() + "'已存在！");
        }

    }

    /**
     * 批量删除试卷
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<String> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        // 试卷列表
        List<TJuanPaper> juanPapers = tJuanPaperMapper.selectBatchIds(ids);
        Boolean flag = tJuanPaperMapper.deleteBatchIds(ids) > 0;
//        if (flag) {
//            // 删除试卷下的历史留存记录
//            List<TJuanPaperBakeup> juanPaperBakeups = tJuanPaperBakeupMapper.selectList(new LambdaQueryWrapper<TJuanPaperBakeup>()
//                .in(TJuanPaperBakeup::getPaperId, ids));
//            List<String> backupIds = juanPaperBakeups.stream().map(TJuanPaperBakeup::getId).collect(Collectors.toList());
//            if (CollectionUtils.isNotEmpty(juanPaperBakeups)) {
//                tJuanPaperBakeupMapper.deleteBatchIds(backupIds);
//                tJuanPaperBakeupDetailMapper.delete(new LambdaQueryWrapper<TJuanPaperBakeupDetail>()
//                    .in(TJuanPaperBakeupDetail::getBakeupId, backupIds));
//            }
//
//            // 新增试卷维护记录
////            insertPaperRecordBatch(PaperRecordType.DELETE, juanPapers);
//        }
        return flag;
    }

    /**
     * 导入试卷
     *
     * @param paperImportBeans 试卷导入信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void importData(List<PaperImportBean> paperImportBeans) {
        // 默认获取第一条，只有第一条才会包含试卷信息
        PaperImportBean importBean = paperImportBeans.get(0);
        // 检查试卷导入信息
        checkPaperInfo(importBean);
        // 题库列表
        List<TJuanQuestionRepos> juanQuestionReposList = tJuanQuestionReposMapper.selectList();
        // 课程列表
        List<TJuanCourse> juanCourses = tJuanCourseMapper.selectList();
        // 知识点列表
        List<TJuanKnowledge> juanKnowledgeList = tJuanKnowledgeMapper.selectList();
        // 年级字典数据列表
        List<SysDictData> questionGrades = sysDictDataMapper.selectDictDataByType(DictTypeConstants.GRADE);
        // 题目类型字典数据列表
        List<SysDictData> questionTypes = sysDictDataMapper.selectDictDataByType(DictTypeConstants.QUESTION_TYPE);
        // 难易程度字典数据列表
        List<SysDictData> questionEasyList = sysDictDataMapper.selectDictDataByType(DictTypeConstants.DIFFICULTY);
        // 课程知识点编码列表
        List<CourseKnowledgeCodeBean> codeBeanList = tJuanCourseMapper.queryCourseKnowledgeCodeList();
        // 题目列表
        List<TJuanQuestion> juanQuestions = tJuanQuestionMapper.selectList();
        // 题目map，key：题目编码，value：题目信息
//        Map<String, TJuanQuestion> questionMap = juanQuestions.stream().collect(
//            Collectors.toMap(TJuanQuestion::getQuestionCode, Function.identity()));
        // 已存在的题目编码列表
        List<String> existQuestionCodes = juanQuestions.stream().map(TJuanQuestion::getQuestionCode).collect(Collectors.toList());
        // 待修改的题目列表
        List<PaperImportBean> questionsForUpdate = Lists.newArrayList();
        // 待新增的题目列表
        List<PaperImportBean> questionsForInsert = Lists.newArrayList();
        int i = 4;
        for (PaperImportBean paperImportBean : paperImportBeans) {
            // 检查题库编码
            checkQuestionReposCode(juanQuestionReposList, paperImportBean.getReposCode(), i);
            // 检查课程编码
            checkCourseCode(juanCourses, paperImportBean.getCourseCode(), i);
            // 检查知识点编码
            checkKnowledgeCode(codeBeanList, paperImportBean.getCourseCode(), paperImportBean.getKnowledgeCode(), i);
            // 检查题目编码
            checkQuestionCode(paperImportBean.getQuestionCode(), i);
            // 检查年级
            checkQuestionGrade(questionGrades, paperImportBean.getQuestionGrade(), i);
            // 检查题目类型
            checkQuestionType(questionTypes, paperImportBean.getQuestionType(), i);
            // 检查难易程度
            checkQuestionEasy(questionEasyList, paperImportBean.getQuestionEasy(), i);
            // 检查难度系数
            checkQuestionDifficulty(paperImportBean.getQuestionDifficult(), i);
            // 检查题目分数
            checkQuestionScore(paperImportBean.getQuestionScore(), i);
            // 检查选项内容JSON
            checkQuestionContent(paperImportBean.getQuestionContent(), i);
            // 判断题目是否已存在数据库
            if (existQuestionCodes.contains(paperImportBean.getQuestionCode())) {
                questionsForUpdate.add(paperImportBean);
            } else {
                questionsForInsert.add(paperImportBean);
            }
            i++;
        }

        List<PaperQuestionInfoBean> questionInfoBeans = Lists.newArrayList();

        // 批量添加题目
        insertQuestionsBatch(juanQuestionReposList, juanCourses, juanKnowledgeList, questionTypes, questionEasyList,
            questionGrades, questionsForInsert, questionInfoBeans);
        // 批量修改题目
        updateQuestionsBatch(questionTypes, questionEasyList, questionGrades, questionsForUpdate, questionInfoBeans);
        // 新增或修改试卷信息
        insertOrUpdateJuanPaper(importBean, questionInfoBeans);
    }

    /**
     * 根据试卷id获取试卷详情
     *
     * @param paperId 试卷id
     * @return 试卷详情
     */
    @Override
    public PaperDetailBean queryPaperDetailById(String paperId) {
        // 试卷详情
        PaperDetailBean paperDetail = tJuanPaperMapper.queryPaperDetailById(paperId);
        // 构建试卷详情
        buildJuanPaper(paperDetail);

        return paperDetail;
    }

    /**
     * 新增试卷信息
     *
     * @param importBean        试卷基本信息
     * @param questionInfoBeans 试卷题目信息
     */
    private void insertOrUpdateJuanPaper(PaperImportBean importBean, List<PaperQuestionInfoBean> questionInfoBeans) {
        // 试卷总分
        int paperTotalScore = questionInfoBeans.stream().mapToInt(PaperQuestionInfoBean::getScore).sum();
        LambdaQueryWrapper<TJuanPaper> lqw = new LambdaQueryWrapper<>();
        lqw.eq(TJuanPaper::getPaperName, importBean.getPaperName());
        List<TJuanPaper> juanPapers = tJuanPaperMapper.selectList(lqw);
        // 试卷信息
        TJuanPaper juanPaper = new TJuanPaper();
        if (0 == juanPapers.size()) {
            juanPaper.setPaperName(importBean.getPaperName());
            juanPaper.setPaperType(GeneratePaperType.IMPORT.getKey());
            juanPaper.setPaperTotalScore(paperTotalScore);
            juanPaper.setPaperQuestionInfo(buildQuestionInfoJsons(questionInfoBeans));
            juanPaper.setPaperTime(importBean.getPaperTime());
            juanPaper.setPaperDes(importBean.getPaperDes());
            juanPaper.setPaperAttention(importBean.getPaperAttention());
            juanPaper.setPaperSealLine(Objects.requireNonNull(YesOrNo.getByValue(importBean.getPaperSealLine())).getKey());
            juanPaper.setCreatedBy(String.valueOf(LoginHelper.getUserId()));
            juanPaper.setCreatedTime(DateUtils.getNowDate());
            // 新增试卷
            if (tJuanPaperMapper.insert(juanPaper) > 0) {
                // 新增试卷维护记录
//                insertPaperRecordBatch(PaperRecordType.IMPORT, Collections.singletonList(juanPaper));
            }
        } else {
            juanPaper = juanPapers.get(0);
            juanPaper.setPaperDes(importBean.getPaperDes());
            juanPaper.setPaperAttention(importBean.getPaperAttention());
            juanPaper.setPaperTotalScore(paperTotalScore);
            juanPaper.setPaperQuestionInfo(buildQuestionInfoJsons(questionInfoBeans));
            juanPaper.setPaperSealLine(Objects.requireNonNull(YesOrNo.getByValue(importBean.getPaperSealLine())).getKey());
            juanPaper.setPaperTime(importBean.getPaperTime());
            juanPaper.setUpdatedBy(String.valueOf(LoginHelper.getUserId()));
            juanPaper.setUpdatedTime(DateUtils.getNowDate());
            // 修改试卷
            if (tJuanPaperMapper.updateById(juanPaper) > 0) {
                // 新增试卷维护记录
//                insertPaperRecordBatch(PaperRecordType.UPDATE, Collections.singletonList(juanPaper));
            }
        }
    }

    /**
     * 批量添加题目
     *
     * @param juanQuestionReposList 题库列表
     * @param juanCourses           课程列表
     * @param juanKnowledgeList     知识点列表
     * @param questionTypes         题目类型列表
     * @param questionEasyList      难易程度列表
     * @param questionGrades        年级列表
     * @param questionsForInsert    待新增的题目列表
     * @param questionInfoBeans     试卷题目信息列表，转成json保存至试卷表
     */
    private void insertQuestionsBatch(List<TJuanQuestionRepos> juanQuestionReposList, List<TJuanCourse> juanCourses,
                                      List<TJuanKnowledge> juanKnowledgeList, List<SysDictData> questionTypes,
                                      List<SysDictData> questionEasyList, List<SysDictData> questionGrades,
                                      List<PaperImportBean> questionsForInsert, List<PaperQuestionInfoBean> questionInfoBeans) {
        long maxId = Long.parseLong(tJuanQuestionMapper.queryMaxId());
        if (0 == questionsForInsert.size()) {
            return;
        }
        // 题库编码map，key：题库编码，vale：题库id
        Map<String, String> questionReposMap = juanQuestionReposList.stream().collect(Collectors.toMap(TJuanQuestionRepos::getReposCode, TJuanQuestionRepos::getReposId));
        // 课程编码map，key：课程编码，vale：课程id
        Map<String, String> courseCodeMap = juanCourses.stream().collect(Collectors.toMap(TJuanCourse::getCourseCode, TJuanCourse::getCourseId));
        // 知识点编码map，key：知识点编码，vale：知识点id
        Map<String, String> knowledgeCodeMap = juanKnowledgeList.stream().collect(Collectors.toMap(TJuanKnowledge::getKnowledgeCode, TJuanKnowledge::getKnowledgeId));
        // 题目类型map，key：题目类型名称，vale：题目类型键值
        Map<String, String> questionTypeMap = questionTypes.stream().collect(Collectors.toMap(SysDictData::getDictLabel, SysDictData::getDictValue));
        // 难易程度map，key：难易程度名称，vale：难易程度键值
        Map<String, String> questionEasyMap = questionEasyList.stream().collect(Collectors.toMap(SysDictData::getDictLabel, SysDictData::getDictValue));
        // 年级map，key：年级名称，vale：年级键值
        Map<String, String> questionGradeMap = questionGrades.stream().collect(Collectors.toMap(SysDictData::getDictLabel, SysDictData::getDictValue));

        // 题目列表
        List<TJuanQuestion> questions = Lists.newArrayList();

        // 题库题目关系列表
        List<TJuanReposRelated> juanReposRelatedList = Lists.newArrayList();
        for (PaperImportBean paperImportBean : questionsForInsert) {
            // 题目信息
            TJuanQuestion juanQuestion = new TJuanQuestion();
            juanQuestion.setQuestionId(String.valueOf(++maxId));
            juanQuestion.setCourseId(courseCodeMap.get(paperImportBean.getCourseCode()));
            juanQuestion.setKnowledgeId(knowledgeCodeMap.get(paperImportBean.getKnowledgeCode()));
            juanQuestion.setQuestionCode(paperImportBean.getQuestionCode());
            juanQuestion.setQuestionType(questionTypeMap.get(paperImportBean.getQuestionType()));
            juanQuestion.setQuestionDifficult(new BigDecimal(paperImportBean.getQuestionDifficult()));
            juanQuestion.setQuestionEasy(questionEasyMap.get(paperImportBean.getQuestionEasy()));
            juanQuestion.setQuestionGrade(questionGradeMap.get(paperImportBean.getQuestionGrade()));
            juanQuestion.setQuestionContent(paperImportBean.getQuestionContent());
            juanQuestion.setCreatedBy(String.valueOf(LoginHelper.getUserId()));
            juanQuestion.setCreatedTime(DateUtils.getNowDate());
            questions.add(juanQuestion);

            // 题库题目关系
            TJuanReposRelated juanReposRelated = new TJuanReposRelated();
            juanReposRelated.setReposId(questionReposMap.get(paperImportBean.getReposCode()));
            juanReposRelated.setQuestionId(juanQuestion.getQuestionId());
            juanReposRelated.setCreatedBy(String.valueOf(LoginHelper.getUserId()));
            juanReposRelated.setCreatedTime(DateUtils.getNowDate());
            juanReposRelatedList.add(juanReposRelated);

            // 试卷题目信息
            PaperQuestionInfoBean paperQuestionInfoBean = new PaperQuestionInfoBean();
            paperQuestionInfoBean.setQuestionId(juanQuestion.getQuestionId());
            paperQuestionInfoBean.setQuestionType(juanQuestion.getQuestionType());
            paperQuestionInfoBean.setQuestionEasy(juanQuestion.getQuestionEasy());
            paperQuestionInfoBean.setScore(paperImportBean.getQuestionScore());
            questionInfoBeans.add(paperQuestionInfoBean);
        }
        // 批量新增题目
        tJuanQuestionMapper.insertBatch(questions);
        // 批量新增题库题目关系
        tJuanReposRelatedMapper.insertBatch(juanReposRelatedList);
    }

    /**
     * 批量修改题目
     *
     * @param questionTypes      题目类型列表
     * @param questionEasyList   难易程度列表
     * @param questionGrades     年级列表
     * @param questionsForUpdate 待修改的题目列表
     * @param questionInfoBeans  试卷题目信息列表，转成json保存至试卷表
     */
    private void updateQuestionsBatch(List<SysDictData> questionTypes, List<SysDictData> questionEasyList,
                                      List<SysDictData> questionGrades, List<PaperImportBean> questionsForUpdate,
                                      List<PaperQuestionInfoBean> questionInfoBeans) {
        if (0 == questionsForUpdate.size()) {
            return;
        }
        // 题目类型map，key：题目类型名称，vale：题目类型键值
        Map<String, String> questionTypeMap = questionTypes.stream().collect(Collectors.toMap(SysDictData::getDictLabel, SysDictData::getDictValue));
        // 难易程度map，key：难易程度名称，vale：难易程度键值
        Map<String, String> questionEasyMap = questionEasyList.stream().collect(Collectors.toMap(SysDictData::getDictLabel, SysDictData::getDictValue));
        // 年级map，key：年级名称，vale：年级键值
        Map<String, String> questionGradeMap = questionGrades.stream().collect(Collectors.toMap(SysDictData::getDictLabel, SysDictData::getDictValue));
        // 题目编码列表
        List<String> questionCodes = questionsForUpdate.stream().map(PaperImportBean::getQuestionCode).collect(Collectors.toList());

        // 题目编码map，key：题目编码，value：题目导入信息
        Map<String, PaperImportBean> questionCodeMap = questionsForUpdate.stream().collect(Collectors.toMap(PaperImportBean::getQuestionCode, Function.identity()));

        LambdaQueryWrapper<TJuanQuestion> lqw = new LambdaQueryWrapper<>();
        lqw.in(TJuanQuestion::getQuestionCode, questionCodes);
        List<TJuanQuestion> questions = tJuanQuestionMapper.selectList(lqw);
        for (TJuanQuestion juanQuestion : questions) {
            PaperImportBean paperImportBean = questionCodeMap.get(juanQuestion.getQuestionCode());
            juanQuestion.setQuestionType(questionTypeMap.get(paperImportBean.getQuestionType()));
            juanQuestion.setQuestionDifficult(new BigDecimal(paperImportBean.getQuestionDifficult()));
            juanQuestion.setQuestionEasy(questionEasyMap.get(paperImportBean.getQuestionEasy()));
            juanQuestion.setQuestionGrade(questionGradeMap.get(paperImportBean.getQuestionGrade()));
            juanQuestion.setQuestionContent(paperImportBean.getQuestionContent());
            juanQuestion.setUpdatedBy(String.valueOf(LoginHelper.getUserId()));
            juanQuestion.setUpdatedTime(DateUtils.getNowDate());

            // 试卷题目信息
            PaperQuestionInfoBean paperQuestionInfoBean = new PaperQuestionInfoBean();
            paperQuestionInfoBean.setQuestionId(juanQuestion.getQuestionId());
            paperQuestionInfoBean.setQuestionType(juanQuestion.getQuestionType());
            paperQuestionInfoBean.setQuestionEasy(juanQuestion.getQuestionEasy());
            paperQuestionInfoBean.setScore(paperImportBean.getQuestionScore());
            questionInfoBeans.add(paperQuestionInfoBean);
        }

        // 批量修改
        tJuanQuestionMapper.updateBatchById(questions);
    }

    /**
     * 根据试卷id列表获取试卷名称列表
     *
     * @param ids 试卷id列表
     * @return 试卷名称列表
     */
    @Override
    public List<String> queryPaperNamesByIds(List<String> ids) {
        // 试卷列表
        List<TJuanPaper> juanPapers = tJuanPaperMapper.selectBatchIds(ids);
        return juanPapers.stream().map(TJuanPaper::getPaperName).collect(Collectors.toList());
    }

    /**
     * 根据课程名称随机抽卷
     *
     * @param bean 随机抽取试卷bean
     * @return 试卷列表
     */
    @Override
    public List<TJuanPaper> randomTakeOutPaper(RandomTakeOutPaperBean bean) {
        // 抽卷份数
        int number = (null == bean.getNumber()) ? 1 : bean.getNumber();
        // 课程id
        String courseId = bean.getCourseId();
        // 课程下的未被抽卷的试卷列表
        List<TJuanPaper> unBeenTakenOutPaperList = tJuanPaperMapper.selectList(new LambdaQueryWrapper<TJuanPaper>()
            .eq(TJuanPaper::getCourseId, courseId)
            .eq(TJuanPaper::getIsTakenOut, YesOrNo.NO.getKey()));
        if (CollectionUtils.isEmpty(unBeenTakenOutPaperList)) {
            throw new ServiceException("当前卷库已被抽取完！");
        }
        if (number > unBeenTakenOutPaperList.size()) {
            throw new ServiceException("抽取的试卷份数过大！");
        }
        Set<String> existPaperIds = new TreeSet<>();
        List<TJuanPaper> randomPapers = Lists.newArrayList();
        while (existPaperIds.size() < number) {
            Random random = new Random();
            // 随机抽卷
            TJuanPaper randomPaper = unBeenTakenOutPaperList.get(random.nextInt(unBeenTakenOutPaperList.size()));
            if (!existPaperIds.contains(randomPaper.getPaperId())) {
                randomPapers.add(randomPaper);
                existPaperIds.add(randomPaper.getPaperId());
            }
        }
        // 批量修改试卷是否被抽取状态
        updatePaperIsTakenOutBatch(randomPapers, YesOrNo.YES);
        // 新增试卷维护记录
//        insertPaperRecordBatch(PaperRecordType.TAKE_OUT, randomPapers);
        return randomPapers;
    }

    /**
     * 根据试卷id获取试卷统计数据
     *
     * @param paperId 试卷id
     * @return 试卷统计数据
     */
    @Override
    public PaperStatisticsBean queryPaperStatisticsById(String paperId) {
        TJuanPaper juanPaper = tJuanPaperMapper.selectById(paperId);
        PaperStatisticsBean bean = new PaperStatisticsBean();
        // 试卷名称
        bean.setName(juanPaper.getPaperName());
        // 难度系数
        bean.setPaperEasy(juanPaper.getPaperEasy());
        String paperQuestionInfo = juanPaper.getPaperQuestionInfo();
        List<PaperQuestionInfoBean> questionInfoBeans = getQuestions(paperQuestionInfo);
        // 题目分数map
        Map<String, Integer> questionScoreMap = questionInfoBeans.stream().collect(Collectors.toMap(PaperQuestionInfoBean::getQuestionId, PaperQuestionInfoBean::getScore));
        // 题目id列表
        List<String> questionIds = questionInfoBeans.stream().map(PaperQuestionInfoBean::getQuestionId).distinct().collect(Collectors.toList());
        // 题目列表
        List<TJuanQuestion> questions = tJuanQuestionMapper.selectBatchIds(questionIds);
        // 试卷总题目数量
        bean.setCount(questions.size());
        questions.forEach(item -> item.setScore(questionScoreMap.get(item.getQuestionId())));
        // 试卷总分
        int totalScore = questions.stream().mapToInt(TJuanQuestion::getScore).sum();
        bean.setScore(totalScore);
        // 章节map,key：章节id，value：题目数量
        Map<String, Long> chapterMap = questions.stream().collect(Collectors.groupingBy(TJuanQuestion::getChapterId, Collectors.counting()));
        // 知识点map,key：知识点id，value：题目数量
        Map<String, Long> knowledgeMap = questions.stream().collect(Collectors.groupingBy(TJuanQuestion::getKnowledgeId, Collectors.counting()));
        // 题目类型map,key：题目类型，value：题目数量
        Map<String, Long> questionTypeMap = questions.stream().collect(Collectors.groupingBy(TJuanQuestion::getQuestionType, Collectors.counting()));
        // 难易程度map,key：难易程度，value：题目数量
        Map<String, Long> questionDifficultMap = questions.stream().collect(Collectors.groupingBy(TJuanQuestion::getQuestionEasy, Collectors.counting()));
        // 能力层次map,key：能力层次，value：题目数量
        Map<String, Long> questionCapableMap = questions.stream().collect(Collectors.groupingBy(TJuanQuestion::getCapabilityLevel, Collectors.counting()));
        // 重要性map,key：重要性，value：题目数量
        Map<String, Long> questionImportanceMap = questions.stream().collect(Collectors.groupingBy(TJuanQuestion::getQuestionGrade, Collectors.counting()));

        // 章节统计
        List<PaperStatisticsFieldBean> chapters = Lists.newArrayList();
        // 章节id列表
        Set<String> chapterIds = chapterMap.keySet();
        List<TJuanChapter> juanChapters = tJuanChapterMapper.selectBatchIds(new ArrayList<>(chapterIds));
        for (TJuanChapter juanChapter : juanChapters) {
            PaperStatisticsFieldBean chapterFieldStatisticsBean = new PaperStatisticsFieldBean();
            chapterFieldStatisticsBean.setValue(juanChapter.getChapterId());
            chapterFieldStatisticsBean.setLabel(juanChapter.getChapterName());
            chapterFieldStatisticsBean.setCount(Math.toIntExact(chapterMap.get(juanChapter.getChapterId())));
            long score = questions.stream().filter(item -> juanChapter.getChapterId().equals(item.getChapterId())).mapToInt(TJuanQuestion::getScore).sum();
            chapterFieldStatisticsBean.setScore((int) score);
            chapterFieldStatisticsBean.setCountRate((float) chapterFieldStatisticsBean.getCount() / (float) bean.getCount());
            chapterFieldStatisticsBean.setScoreRate((float) score / (float) totalScore);
            chapters.add(chapterFieldStatisticsBean);
        }
        bean.setChapters(chapters);

        // 知识点统计
        List<PaperStatisticsFieldBean> knowledgeList = Lists.newArrayList();
        // 知识点id列表
        Set<String> knowledgeIds = knowledgeMap.keySet();
        List<TJuanKnowledge> juanKnowledgeList = tJuanKnowledgeMapper.selectBatchIds(new ArrayList<>(knowledgeIds));
        for (TJuanKnowledge juanKnowledge : juanKnowledgeList) {
            PaperStatisticsFieldBean knowledgeFieldStatisticsBean = new PaperStatisticsFieldBean();
            knowledgeFieldStatisticsBean.setValue(juanKnowledge.getKnowledgeId());
            knowledgeFieldStatisticsBean.setLabel(juanKnowledge.getKnowledgeName());
            knowledgeFieldStatisticsBean.setCount(Math.toIntExact(knowledgeMap.get(juanKnowledge.getKnowledgeId())));
            long score = questions.stream().filter(item -> juanKnowledge.getKnowledgeId().equals(item.getKnowledgeId())).mapToInt(TJuanQuestion::getScore).sum();
            knowledgeFieldStatisticsBean.setScore((int) score);
            knowledgeFieldStatisticsBean.setCountRate((float) knowledgeFieldStatisticsBean.getCount() / (float) bean.getCount());
            knowledgeFieldStatisticsBean.setScoreRate((float) score / (float) totalScore);
            knowledgeList.add(knowledgeFieldStatisticsBean);
        }
        bean.setKnowledgeList(knowledgeList);

        // 题目类型统计
        List<PaperStatisticsFieldBean> questionTypes = Lists.newArrayList();
        // 题目类型列表
        List<SysDictData> questionTypeDictDataList = sysDictDataMapper.selectDictDataByType(DictTypeConstants.QUESTION_TYPE);
        for (SysDictData data : questionTypeDictDataList) {
            PaperStatisticsFieldBean typeFieldStatisticsBean = new PaperStatisticsFieldBean();
            typeFieldStatisticsBean.setValue(data.getDictValue());
            typeFieldStatisticsBean.setLabel(data.getDictLabel());
            typeFieldStatisticsBean.setCount(null == questionTypeMap.get(data.getDictValue()) ?
                0 : Math.toIntExact(questionTypeMap.get(data.getDictValue())));
            if (0 == typeFieldStatisticsBean.getCount()) {
                continue;
            }
            long score = questions.stream().filter(item -> data.getDictValue().equals(item.getQuestionType())).mapToInt(TJuanQuestion::getScore).sum();
            typeFieldStatisticsBean.setScore((int) score);
            typeFieldStatisticsBean.setCountRate((float) typeFieldStatisticsBean.getCount() / (float) bean.getCount());
            typeFieldStatisticsBean.setScoreRate((float) score / (float) totalScore);
            questionTypes.add(typeFieldStatisticsBean);
        }
        bean.setQuestionTypes(questionTypes);

        // 难易程度统计
        List<PaperStatisticsFieldBean> questionDifficultList = Lists.newArrayList();
        // 难易程度列表
        List<SysDictData> difficultDictDataList = sysDictDataMapper.selectDictDataByType(DictTypeConstants.DIFFICULTY);
        for (SysDictData data : difficultDictDataList) {
            PaperStatisticsFieldBean difficultFieldStatisticsBean = new PaperStatisticsFieldBean();
            difficultFieldStatisticsBean.setValue(data.getDictValue());
            difficultFieldStatisticsBean.setLabel(data.getDictLabel());
            difficultFieldStatisticsBean.setCount(null == questionDifficultMap.get(data.getDictValue()) ?
                0 : Math.toIntExact(questionDifficultMap.get(data.getDictValue())));
            if (0 == difficultFieldStatisticsBean.getCount()) {
                continue;
            }
            long score = questions.stream().filter(item -> data.getDictValue().equals(item.getQuestionEasy())).mapToInt(TJuanQuestion::getScore).sum();
            difficultFieldStatisticsBean.setScore((int) score);
            difficultFieldStatisticsBean.setCountRate((float) difficultFieldStatisticsBean.getCount() / (float) bean.getCount());
            difficultFieldStatisticsBean.setScoreRate((float) score / (float) totalScore);
            questionDifficultList.add(difficultFieldStatisticsBean);
        }
        bean.setQuestionDifficultList(questionDifficultList);

        // 能力层次统计
        List<PaperStatisticsFieldBean> questionCapableList = Lists.newArrayList();
        // 能力层次列表
        List<SysDictData> capabilityLevels = sysDictDataMapper.selectDictDataByType(DictTypeConstants.CAPABILITY_LEVEL);
        for (SysDictData data : capabilityLevels) {
            PaperStatisticsFieldBean capableFieldStatisticsBean = new PaperStatisticsFieldBean();
            capableFieldStatisticsBean.setCount(null == questionCapableMap.get(data.getDictValue()) ?
                0 : Math.toIntExact(questionCapableMap.get(data.getDictValue())));
            if (0 == capableFieldStatisticsBean.getCount()) {
                continue;
            }
            capableFieldStatisticsBean.setValue(data.getDictValue());
            capableFieldStatisticsBean.setLabel(data.getDictLabel());
            long score = questions.stream().filter(item -> data.getDictValue().equals(item.getCapabilityLevel())).mapToInt(TJuanQuestion::getScore).sum();
            capableFieldStatisticsBean.setScore((int) score);
            capableFieldStatisticsBean.setCountRate((float) capableFieldStatisticsBean.getCount() / (float) bean.getCount());
            capableFieldStatisticsBean.setScoreRate((float) score / (float) totalScore);
            questionCapableList.add(capableFieldStatisticsBean);
        }
        bean.setQuestionCapableList(questionCapableList);

        // 重要性统计
        List<PaperStatisticsFieldBean> questionImportanceList = Lists.newArrayList();
        // 重要性列表
        List<SysDictData> importanceList = sysDictDataMapper.selectDictDataByType(DictTypeConstants.GRADE);
        for (SysDictData data : importanceList) {
            PaperStatisticsFieldBean statisticsFieldBean = new PaperStatisticsFieldBean();
            statisticsFieldBean.setCount(null == questionImportanceMap.get(data.getDictValue()) ?
                0 : Math.toIntExact(questionImportanceMap.get(data.getDictValue())));
            if (0 == statisticsFieldBean.getCount()) {
                continue;
            }
            statisticsFieldBean.setValue(data.getDictValue());
            statisticsFieldBean.setLabel(data.getDictLabel());
            long score = questions.stream().filter(item -> data.getDictValue().equals(item.getQuestionGrade())).mapToInt(TJuanQuestion::getScore).sum();
            statisticsFieldBean.setScore((int) score);
            statisticsFieldBean.setCountRate((float) statisticsFieldBean.getCount() / (float) bean.getCount());
            statisticsFieldBean.setScoreRate((float) score / (float) totalScore);
            questionImportanceList.add(statisticsFieldBean);
        }
        bean.setQuestionImportanceList(questionImportanceList);

        // 相关性统计
        QuestionVO questionRelevance = new QuestionVO();
        List<String> questionCodes = questions.stream().map(TJuanQuestion::getQuestionCode).collect(Collectors.toList());
        List<TJuanQuestion> questionsForRelevance = questions.stream().filter(item -> null != item.getRelevance() && questionCodes.contains(item.getRelevance())).distinct().collect(Collectors.toList());
        questionRelevance.setQuestionList(questionsForRelevance);
        questionRelevance.setRate(0 == questionsForRelevance.size() ? 0 : (float) questionsForRelevance.size() / (float) bean.getCount());
        bean.setQuestionRelevance(questionRelevance);

        return bean;
    }

    /**
     * 导出试卷json信息
     *
     * @param paperId  试卷id
     * @param response 响应
     */
    @Override
    public void exportPaperJson(String paperId, HttpServletResponse response) {
        TJuanPaper juanPaper = tJuanPaperMapper.selectById(paperId);
        String courseId = juanPaper.getCourseId();
        LambdaQueryWrapper<TJuanQuestion> tq = Wrappers.lambdaQuery();
        LambdaQueryWrapper<TJuanChapter> tc = Wrappers.lambdaQuery();
        // 题库题目列表
        List<TJuanQuestion> questionList = tJuanQuestionMapper.selectList(tq.eq(TJuanQuestion::getCourseId, courseId));
        // 课程信息
        TJuanCourse tJuanCourse = tJuanCourseMapper.selectById(courseId);
        // 章节列表
        List<TJuanChapter> tJuanChapters = tJuanChapterMapper.selectList(tc.eq(TJuanChapter::getCourseId, courseId));
        // 知识点列表
        List<TJuanKnowledge> tJuanKnowledgeList = tJuanKnowledgeMapper.selectByCourseId(courseId);

        PaperJsonBean paperJsonBean = new PaperJsonBean();
        paperJsonBean.setJuanPaper(juanPaper);
        paperJsonBean.setJuanCourse(tJuanCourse);
        paperJsonBean.setJuanChapterList(tJuanChapters);
        paperJsonBean.setJuanKnowledgeList(tJuanKnowledgeList);
        paperJsonBean.setQuestionList(questionList);
        String json = JsonUtils.toJsonString(paperJsonBean);
        response.setContentType("application/octet-stream");
        response.setHeader("Content-disposition", "attachment;filename=juanPaper.json");
        try {
            response.getOutputStream().write(AesUtil.aesEncrypt(json).getBytes());
//            response.getOutputStream().write(json.getBytes());
        } catch (IOException e) {
            e.printStackTrace();

        }
    }

    /**
     * 导入试卷json信息
     *
     * @param file 试卷json文件
     */
    @Override
    public void importPaperJson(MultipartFile file) {
        try {
            InputStream inputStream = file.getInputStream();
            String string = IOUtils.toString(inputStream);
            PaperJsonBean jsonBean = JsonUtils.parseObject(AesUtil.aesDecrypt(string), PaperJsonBean.class);
            TJuanPaper juanPaper = jsonBean.getJuanPaper();
            TJuanCourse juanCourse = jsonBean.getJuanCourse();
            List<TJuanChapter> juanChapterList = jsonBean.getJuanChapterList();
            List<TJuanKnowledge> juanKnowledgeList = jsonBean.getJuanKnowledgeList();
            List<TJuanQuestion> questionList = jsonBean.getQuestionList();
            if (null == juanPaper || null == juanCourse || CollectionUtils.isEmpty(juanChapterList)
                || CollectionUtils.isEmpty(juanKnowledgeList) || CollectionUtils.isEmpty(questionList)) {
                throw new ServiceException("数据包格式错误！");
            }
            juanPaper.setPaperType(GeneratePaperType.IMPORT.getKey());
            juanPaper.setIsTakenOut(YesOrNo.NO.getKey());
            juanPaper.setCreatedTime(DateUtils.getNowDate());
            // 试卷插入或更新
            tJuanPaperMapper.insertOrUpdate(juanPaper);
            // 题库插入或更新
            tJuanCourseMapper.insertOrUpdate(juanCourse);
            // 章节知识点插入或更新
            tJuanChapterMapper.insertOrUpdateBatch(juanChapterList);
            tJuanKnowledgeMapper.insertOrUpdateBatch(juanKnowledgeList);
            // 题目插入或更新
            tJuanQuestionMapper.insertOrUpdateBatch(questionList);
//            insertPaperRecordBatch(PaperRecordType.IMPORT, Collections.singletonList(juanPaper));
        } catch (RuntimeException | IOException e) {
            e.printStackTrace();
            throw new ServiceException("数据包格式错误！");
        }
    }

    /**
     * 批量修改试卷是否被抽取状态
     *
     * @param juanPapers 试卷列表
     * @param yesOrNo    是或否枚举
     */
    private void updatePaperIsTakenOutBatch(List<TJuanPaper> juanPapers, YesOrNo yesOrNo) {
        juanPapers.forEach(item -> item.setIsTakenOut(yesOrNo.getKey()));
        tJuanPaperMapper.updateBatchById(juanPapers);
    }

    /**
     * 新增试卷维护记录
     *
     * @param paperRecordType 试卷维护类型
     * @param juanPapers      试卷列表
     */
    private void insertPaperRecordBatch(PaperRecordType paperRecordType, List<TJuanPaper> juanPapers) {
        // 试卷维护记录列表
        ArrayList<TJuanPaperRecord> paperRecords = Lists.newArrayList();
        for (TJuanPaper juanPaper : juanPapers) {
            TJuanPaperRecord juanPaperRecord = new TJuanPaperRecord();
            juanPaperRecord.setPaperId(juanPaper.getPaperId());
            juanPaperRecord.setCourseId(juanPaper.getCourseId());
            juanPaperRecord.setPaperName(juanPaper.getPaperName());
            juanPaperRecord.setRecordType(paperRecordType.getKey());
            juanPaperRecord.setRecordContent(LoginHelper.getUsername() + paperRecordType.getValue() + "了试卷！");
            juanPaperRecord.setCreatedBy(String.valueOf(LoginHelper.getUserId()));
            juanPaperRecord.setCreatedTime(DateUtils.getNowDate());
            paperRecords.add(juanPaperRecord);
        }
        tJuanPaperRecordMapper.insertBatch(paperRecords);
    }

    /**
     * 检查试卷信息
     *
     * @param paperInfo 试卷信息
     */
    private void checkPaperInfo(PaperImportBean paperInfo) {
        // 检查试卷名称
        checkPaperName(paperInfo.getPaperName());
        // 检查试卷类型
        checkPaperType(paperInfo.getPaperType());
        // 检查试卷描述
        checkPaperDes(paperInfo.getPaperType());
        // 检查试卷注意事项
        checkPaperAttention(paperInfo.getPaperAttention());
        // 检查字段 是否有密封线
        checkPaperSealLine(paperInfo.getPaperSealLine());
        // 检查考试时长
        checkPaperTime(paperInfo.getPaperTime());
        // 检查考试总分
        checkPaperTotalScore(paperInfo.getPaperTotalScore());
    }

    /**
     * 检查试卷名称
     *
     * @param paperName 试卷名称
     */
    private void checkPaperName(String paperName) {
        if (null == paperName || !PaperConstants.PAPER_NAME_PATTERN.matcher(paperName).matches()) {
            throw new ServiceException("“试卷名称”字段长度应为1-255位！");
        }
    }

    /**
     * 检查试卷类型
     *
     * @param paperType 试卷名称
     */
    private void checkPaperType(String paperType) {
        if (null != paperType && null == GeneratePaperType.getByValue(paperType)) {
            throw new ServiceException("请输入正确的试卷类型!");
        }
    }

    /**
     * 检查试卷描述
     *
     * @param paperDes 试卷名称
     */
    private void checkPaperDes(String paperDes) {
        if (null != paperDes && !PaperConstants.PAPER_DES_PATTERN.matcher(paperDes).matches()) {
            throw new ServiceException("“试卷描述”字段长度应为0-255位！");
        }
    }

    /**
     * 检查试卷注意事项
     *
     * @param paperAttention 试卷注意事项
     */
    private void checkPaperAttention(String paperAttention) {
        if (null != paperAttention && !PaperConstants.PAPER_ATTENTION_PATTERN.matcher(paperAttention).matches()) {
            throw new ServiceException("“注意事项”字段长度应为0-900位！");
        }
    }

    /**
     * 检查字段 是否有密封线
     *
     * @param paperSealLine 是否有密封线
     */
    private void checkPaperSealLine(String paperSealLine) {
        if (null == paperSealLine || null == YesOrNo.getByValue(paperSealLine)) {
            throw new ServiceException("是否有密封线字段格式错误！");
        }
    }

    /**
     * 检查考试总分
     *
     * @param paperTotalScore 考试总分
     */
    private void checkPaperTotalScore(Integer paperTotalScore) {
        if (null != paperTotalScore && !PaperConstants.PAPER_TOTAL_SCORE_PATTERN.matcher(paperTotalScore.toString()).matches()) {
            throw new ServiceException("“总分”字段为最多不超过10位数的正整数！");
        }
    }

    /**
     * 检查考试时长
     *
     * @param paperTime 考试时长
     */
    private void checkPaperTime(Integer paperTime) {
        if (null == paperTime) {
            throw new ServiceException("缺少必填字段：考试时长！");
        }
        if (!PaperConstants.PAPER_TIME_PATTERN.matcher(paperTime.toString()).matches()) {
            throw new ServiceException("“考试时长”字段为最多不超过10位数的正整数！");
        }
    }

    /**
     * 检查题库编码
     *
     * @param juanQuestionReposList 题库列表
     * @param reposCode             题库编码
     * @param i                     数据行数
     */
    private void checkQuestionReposCode(List<TJuanQuestionRepos> juanQuestionReposList, String reposCode, Integer i) {
        if (null == reposCode) {
            throw new ServiceException("第" + i + "行数据中，缺少必填字段：题库编码！");
        }
        List<String> reposCodes = juanQuestionReposList.stream().map(TJuanQuestionRepos::getReposCode).collect(Collectors.toList());
        if (!reposCodes.contains(reposCode)) {
            throw new ServiceException("第" + i + "行数据中，题库编码：" + reposCode + "不存在！");
        }
    }

    /**
     * 检查专业名称
     *
     * @param juanMajors 专业列表
     * @param majorName  专业名称
     * @param i          数据行数
     */
    private void checkMajorName(List<TJuanMajor> juanMajors, String majorName, Integer i) {
        if (null == majorName) {
            throw new ServiceException("第" + i + "行数据中，缺少必填字段：专业名称！");
        }
        List<String> majorNames = juanMajors.stream().map(TJuanMajor::getMajorName).collect(Collectors.toList());
        if (!majorNames.contains(majorName)) {
            throw new ServiceException("第" + i + "行数据中，专业名称：" + majorNames + "不存在！");
        }
    }

    /**
     * 检查课程编码
     *
     * @param juanCourses 课程列表
     * @param courseCode  课程编码
     * @param i           数据行数
     */
    private void checkCourseCode(List<TJuanCourse> juanCourses, String courseCode, Integer i) {
        if (null == courseCode) {
            throw new ServiceException("第" + i + "行数据中，缺少必填字段：课程编码！");
        }
        List<String> courseCodes = juanCourses.stream().map(TJuanCourse::getCourseCode).collect(Collectors.toList());
        if (!courseCodes.contains(courseCode)) {
            throw new ServiceException("第" + i + "行数据中，课程编码：" + courseCode + "不存在！");
        }
    }

    /**
     * 检查知识点名称
     *
     * @param codeBeanList  课程知识点编码列表
     * @param courseCode    课程编码
     * @param knowledgeCode 知识点编码
     * @param i             数据行数
     */
    private void checkKnowledgeCode(List<CourseKnowledgeCodeBean> codeBeanList, String courseCode, String knowledgeCode, Integer i) {
        if (null == knowledgeCode) {
            throw new ServiceException("第" + i + "行数据中，缺少必填字段：知识点编码！");
        }
        List<String> knowledgeCodes = codeBeanList.stream().filter(o -> o.getCourseCode().equals(courseCode))
            .map(CourseKnowledgeCodeBean::getKnowledgeCode).collect(Collectors.toList());
        if (!knowledgeCodes.contains(knowledgeCode)) {
            throw new ServiceException("第" + i + "行数据中，课程编码：" + courseCode + "下不存在知识点编码：" + knowledgeCode + "！");
        }
    }

    /**
     * 检查题目编码
     *
     * @param questionCode 题目编码
     * @param i            数据行数
     */
    private void checkQuestionCode(String questionCode, Integer i) {
        if (!QuestionConstants.QUESTION_CODE_PATTERN.matcher(questionCode).matches()) {
            throw new ServiceException("第" + i + "行数据中，题目编码字段长度应为1-255！");
        }
    }

    /**
     * 检查年级
     *
     * @param questionGrade 年级
     * @param i             数据行数
     */
    private void checkQuestionGrade(List<SysDictData> questionGrades, String questionGrade, Integer i) {
        if (null == questionGrade) {
            throw new ServiceException("第" + i + "行数据中，缺少必填字段：年级！");
        }
        List<String> grades = questionGrades.stream().map(SysDictData::getDictLabel).collect(Collectors.toList());
        if (!grades.contains(questionGrade)) {
            throw new ServiceException("第" + i + "行数据中，年级：" + questionGrade + "不存在！");
        }
    }

    /**
     * 检查题目类型
     *
     * @param questionType 题目类型
     * @param i            数据行数
     */
    private void checkQuestionType(List<SysDictData> questionTypes, String questionType, Integer i) {
        if (null == questionType) {
            throw new ServiceException("第" + i + "行数据中，缺少必填字段：题目类型！");
        }
        List<String> questionTypeNames = questionTypes.stream().map(SysDictData::getDictLabel).collect(Collectors.toList());
        if (!questionTypeNames.contains(questionType)) {
            throw new ServiceException("第" + i + "行数据中，题目类型：" + questionType + "不存在！");
        }
    }

    /**
     * 检查难易程度
     *
     * @param questionEasy 难易程度
     * @param i            数据行数
     */
    private void checkQuestionEasy(List<SysDictData> questionEasyList, String questionEasy, Integer i) {
        if (null == questionEasy) {
            throw new ServiceException("第" + i + "行数据中，缺少必填字段：难易程度！");
        }
        List<String> easyNames = questionEasyList.stream().map(SysDictData::getDictLabel).collect(Collectors.toList());
        if (!easyNames.contains(questionEasy)) {
            throw new ServiceException("第" + i + "行数据中，难易程度：" + questionEasy + "不存在！");
        }
    }

    /**
     * 检查难度系数
     *
     * @param questionDifficulty 难度系数
     * @param i                  数据行数
     */
    private void checkQuestionDifficulty(String questionDifficulty, Integer i) {
        if (null == questionDifficulty) {
            throw new ServiceException("第" + i + "行数据中，缺少必填字段：难度系数！");
        }
        if (!QuestionConstants.QUESTION_DIFFICULT__PATTERN.matcher(questionDifficulty).matches()) {
            throw new ServiceException("第" + i + "行数据中，“难度系数”字段为最多不超过1且保留两位数的正数！");
        }
    }

    /**
     * 检查题目分数
     *
     * @param questionScore 题目分数
     * @param i             数据行数
     */
    private void checkQuestionScore(Integer questionScore, Integer i) {
        if (null == questionScore) {
            throw new ServiceException("第" + i + "行数据中，缺少必填字段：分数！");
        }
        if (!QuestionConstants.QUESTION_SCORE_PATTERN.matcher(questionScore.toString()).matches()) {
            throw new ServiceException("第" + i + "行数据中，分数字段为最多不超过10位数的正整数！");
        }
    }

    /**
     * 检查选项内容JSON
     *
     * @param questionContent 选项内容JSON
     * @param i               数据行数
     */
    private void checkQuestionContent(String questionContent, Integer i) {
        if (null == questionContent) {
            throw new ServiceException("第" + i + "行数据中，缺少必填字段：选项内容！");
        }
    }

    /**
     * 构建试卷题目信息json列表
     *
     * @param questionInfoBeans 试卷题目列表
     */
    private String buildQuestionInfoJsons(List<PaperQuestionInfoBean> questionInfoBeans) {
        // 系统字典数据列表
        List<SysDictData> sysDictDataList = sysDictDataMapper.selectDictDataByType(DictTypeConstants.QUESTION_TYPE);
        // 字典数据map（key：字典键值，value：字典标签）
        Map<String, String> dictDataMap = sysDictDataList.stream().collect(Collectors.toMap(SysDictData::getDictValue, SysDictData::getDictLabel));
        // 题型详情列表（转成json，用于存入试卷表）
        List<QuestionTypeJsonBean> questionTypeDetails = Lists.newArrayList();
        Map<String, List<PaperQuestionInfoBean>> collect = questionInfoBeans.stream().collect(Collectors.groupingBy(PaperQuestionInfoBean::getQuestionType));
        for (Map.Entry<String, List<PaperQuestionInfoBean>> entry : collect.entrySet()) {
            // 题目类型
            String questionType = entry.getKey();
            // 题目列表
            List<PaperQuestionInfoBean> questionList = entry.getValue();
            QuestionTypeJsonBean questionTypeJsonBean = new QuestionTypeJsonBean();
            questionTypeJsonBean.setQuestionType(questionType);
            questionTypeJsonBean.setBigName(dictDataMap.get(questionType));
            questionTypeJsonBean.setBigDesc("（本大题共" + questionList.size() + "小题，" +
                "共" + questionList.size() * questionList.get(0).getScore() + "分）");
            questionTypeJsonBean.setQuestions(questionList);
            questionTypeDetails.add(questionTypeJsonBean);
        }
        return JsonUtils.toJsonString(questionTypeDetails);
    }

    /**
     * 构建试卷详情
     *
     * @param paperDetail 试卷详情
     */
    private void buildJuanPaper(PaperDetailBean paperDetail) {
        // 题目类型字典数据列表
        List<SysDictData> questionTypes = sysDictDataMapper.selectDictDataByType(DictTypeConstants.QUESTION_TYPE);
        // 题目类型map，key：题目类型键值，vale：题目类型名称
        Map<String, String> questionTypeMap = questionTypes.stream().collect(Collectors.toMap(SysDictData::getDictValue, SysDictData::getDictLabel));
        // 难易程度字典数据列表
        List<SysDictData> questionEasyList = sysDictDataMapper.selectDictDataByType(DictTypeConstants.DIFFICULTY);
        // 难易程度map，key：难易程度键值，vale：难易程度名称
        Map<String, String> questionEasyMap = questionEasyList.stream().collect(Collectors.toMap(SysDictData::getDictValue, SysDictData::getDictLabel));
        // 能力层次字典数据列表
        List<SysDictData> capabilityLevels = sysDictDataMapper.selectDictDataByType(DictTypeConstants.CAPABILITY_LEVEL);
        // 能力层次map，key：能力层次键值，vale：能力层次名称
        Map<String, String> capabilityLevelMap = capabilityLevels.stream().collect(Collectors.toMap(SysDictData::getDictValue, SysDictData::getDictLabel));
        // 题目信息列表
        List<PaperQuestionInfoBean> questions = getQuestions(paperDetail.getPaperQuestionInfo());
        // 试卷题目信息map，key：题目id，value：题目信息
        Map<String, PaperQuestionInfoBean> paperQuestionInfoMap = questions.stream().collect(Collectors.toMap(PaperQuestionInfoBean::getQuestionId, Function.identity()));
        List<QuestionInfoBean> list = tJuanQuestionMapper.queryQuestionInfosByIds(new ArrayList<>(paperQuestionInfoMap.keySet()));
        if (0 == list.size()) {
            throw new ServiceException("试卷题目列表为空！");
        }
        for (QuestionInfoBean questionInfoBean : list) {
            PaperQuestionInfoBean paperQuestionInfoBean = paperQuestionInfoMap.get(questionInfoBean.getQuestionId());
            questionInfoBean.setQuestionType(questionTypeMap.get(paperQuestionInfoBean.getQuestionType()));
            questionInfoBean.setQuestionEasy(questionEasyMap.get(paperQuestionInfoBean.getQuestionEasy()));
            questionInfoBean.setCapabilityLevel(capabilityLevelMap.get(questionInfoBean.getCapabilityLevel()));
            questionInfoBean.setQuestionScore(paperQuestionInfoBean.getScore());
            BigDecimal questionDifficult = new BigDecimal(questionInfoBean.getQuestionDifficult()).setScale(2, BigDecimal.ROUND_UNNECESSARY);
            questionInfoBean.setQuestionDifficult(questionDifficult.toString());
        }
        paperDetail.setQuestionInfoList(list);
    }

    /**
     * 获取试卷编码
     *
     * @param juanPaper 试卷信息
     * @return 试卷编码
     */
    private String getPaperCode(TJuanPaper juanPaper) {
        // 课程id
        String courseId = juanPaper.getCourseId();
        // 课程信息
        TJuanCourse juanCourse = tJuanCourseMapper.selectById(courseId);
        if (null == juanCourse) {
            throw new ServiceException("课程不存在！");
        }
        // 课程编码
        String courseCode = juanCourse.getCourseCode();
        LambdaQueryWrapper<TJuanPaper> lqw = Wrappers.lambdaQuery();
        lqw.eq(StringUtils.isNotBlank(courseId), TJuanPaper::getCourseId, courseId);
        lqw.orderByDesc(TJuanPaper::getCreatedTime);
        List<TJuanPaper> juanPapers = tJuanPaperMapper.selectList(lqw);
        if (CollectionUtils.isEmpty(juanPapers)) {
            return courseCode + "001";
        }
        // 最新的一份试卷
        TJuanPaper newestPaper = juanPapers.get(0);
        // 最新试卷的试卷编码
        String newestPaperCode = newestPaper.getPaperCode();
        int index = Integer.parseInt(newestPaperCode.substring(newestPaperCode.length() - 3));
        return courseCode + String.format("%03d", index + 1);
    }

    /**
     * 获取题目列表
     *
     * @param paperQuestionInfo 试卷题目信息json
     * @return 题目列表
     */
    private List<PaperQuestionInfoBean> getQuestions(String paperQuestionInfo) {
        List<PaperQuestionInfoBean> questions = Lists.newArrayList();
        List<QuestionTypeJsonBean> questionTypeJsonBeans = JsonUtils.parseArray(paperQuestionInfo, QuestionTypeJsonBean.class);
        for (QuestionTypeJsonBean questionTypeJsonBean : questionTypeJsonBeans) {
            questions.addAll(questionTypeJsonBean.getQuestions());
        }
        return questions;
    }

}
